package com.ezops.poc.validations;

import static com.ezops.poc.routing.TradeRoutingBuilder.HEADER_KEY_IS_VALID_TRADE;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.dozer.DozerBeanMapper;
import org.dozer.loader.api.BeanMappingBuilder;
import org.dozer.loader.api.TypeMappingOptions;
import org.drools.KnowledgeBase;
import org.drools.event.rule.DebugAgendaEventListener;
import org.drools.event.rule.DebugWorkingMemoryEventListener;
import org.drools.runtime.StatefulKnowledgeSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.ezops.poc.AgendaListener;
import com.ezops.poc.bo.EzopsModel;
import com.ezops.poc.bo.EzopsOTEquityModel;
import com.ezops.poc.bo.EzopsOTEquityStaticModel;
import com.ezops.poc.validations.parser.ValidationFileParser;
import com.ezops.poc.validations.result.ValidationResult;
import com.ezops.poc.validations.validators.IValidators;

/**
 * Checks if all the rules for each fields are passed. In case of failure create
 * EzopsValidationExceoptin and add add field errors.
 * 
 * @author vgaurav
 * 
 */
@Component(value = "validationProcessor")
public class ValidationProcessor implements Processor {

	@Autowired
	private ValidationFileParser parser;

	/**
	 * Run the rules on Ezops format data.
	 * 
	 * @throws Exception
	 *             if validation/rule fails.
	 */
	public void process(Exchange exchange) throws Exception {
		EzopsModel model = exchange.getIn().getBody(EzopsModel.class);
		EzopsValidationException returnedValidationException = executeEzopsValidationRule(model);

		if (returnedValidationException != null) {
			exchange.getIn().setHeader(HEADER_KEY_IS_VALID_TRADE, false);
			throw returnedValidationException;
		}

		EzopsValidationException returnedRuleException = executeEzopsRule(convertToStaticModel(model));

		if (returnedRuleException != null) {
			exchange.getIn().setHeader(HEADER_KEY_IS_VALID_TRADE, false);
			throw returnedRuleException;
		}
		exchange.getIn().setHeader(HEADER_KEY_IS_VALID_TRADE, true);
	}

	private EzopsOTEquityStaticModel convertToStaticModel(EzopsModel model) {
		BeanMappingBuilder mappingBuilder = new BeanMappingBuilder() {
			@Override
			protected void configure() {

				String dateFormat = "yyyyMMdd";

				mapping(EzopsOTEquityModel.class,
						EzopsOTEquityStaticModel.class,
						TypeMappingOptions.wildcard(true),
						TypeMappingOptions.dateFormat(dateFormat));

			}
		};

		DozerBeanMapper apiBeanMapper = new DozerBeanMapper();
		apiBeanMapper.addMapping(mappingBuilder);

		return apiBeanMapper.map(model, EzopsOTEquityStaticModel.class);
	}

	public EzopsValidationException executeEzopsValidationRule(EzopsModel model)
			throws EzopsValidationException {
		EzopsValidationException exception = null;

		List<LinkedList<IValidators>> validators = new ArrayList<>(
				parser.ezopsRules.values());

		for (List<IValidators> validatorlist : validators) {

			for (IValidators validator : validatorlist) {
				ValidationResult result = validator.validate(model);
				if (result.isValid()) {
					continue;
				}
				if (exception == null) {
					exception = new EzopsValidationException(
							getIdentifier(model));
				}
				exception.addValidationError(result.getValidationError());
				break; // High priority rule failed , no need to continue the
						// rule in the lined list as they apply to the same
						// field.
			}

		}
		return exception;

	}

	public EzopsValidationException executeEzopsRule(
			EzopsOTEquityStaticModel model) throws EzopsValidationException {
		EzopsValidationException exception = null;

		try {

			KnowledgeBase kbase = com.ezops.poc.util.RulesLoader.ezopsRuleBase;
			StatefulKnowledgeSession ksession = kbase
					.newStatefulKnowledgeSession();

			ksession.addEventListener(new DebugAgendaEventListener());
			ksession.addEventListener(new DebugWorkingMemoryEventListener());
			ksession.addEventListener(new AgendaListener());
			System.err.println(ksession.getKnowledgeBase().getEntryPointIds());
			ksession.insert(model);

			int firecount = ksession.fireAllRules();
		} catch (Throwable t) {
			t.printStackTrace();
		}

		List<ValidationResult> results = model.getErrors();
		if (results == null || results.isEmpty()) {
			return null;
		}
		for (ValidationResult result : results) {
			if (result.isValid()) {
				continue;
			}
			if (exception == null) {
				exception = new EzopsValidationException(model.getIdentifier());
			}
			exception.addValidationError(result.getValidationError());
		}

		return exception;
	}

	private TradeIndentifier getIdentifier(EzopsModel model) {
		TradeIndentifier identifier = new TradeIndentifier();
		identifier.setAction(model.getAction());
		if (model.getTradeId() != null)
			identifier.setTradeId(Integer.parseInt(model.getTradeId()));
		identifier.setFileName(model.getFileName());
		identifier.setStatus("valid");
		identifier.setClientId(model.getClientId());
		identifier.setAssetId(model.getAssetId());
		return identifier;
	}

}
